﻿using Cysharp.Threading.Tasks;
using Net.Client;
using Net.Common;
using Net.Event;
using Net.Helper;
using Net.Server;
using Net.Share;
using Net.System;

namespace WordGames.System
{
    public static class ClusterSystem
    {
        public static Config Config { get; set; }
        public static MyDictionary<string, MyDictionary<string, MyDictionary<string, Cluster>>> Clusters { get; internal set; } = new();
        public static ClientPool<TcpNetwork> ClientPool { get; internal set; } = new();
        public static TcpNetwork Central { get; internal set; }
        public static DBCluster DBCluster { get; internal set; }
        public static List<ServerBase> Services { get; internal set; } = new();

        private static readonly FastLocking centralLock = new();

        static ClusterSystem()
        {
            ClientPool.Init();
            ClientPool.Start();
        }

        public static void Start()
        {
            if (Config == null)
            {
                NDebug.LogError("请设置集群配置!");
                return;
            }
            Start(Config);
        }

        public static void Start(Config config)
        {
            Config = config;
            if (Central == null)
            {
                ConnectCentral().Wait();
                StartDBCluster();
            }
            foreach (var localConfig in config.Local)
            {
                var serverType = AssemblyHelper.GetTypeNotOptimized(localConfig.Name);
                foreach (var host in localConfig.Hosts)
                {
                    var service = (ServerBase)Activator.CreateInstance(serverType);
                    var parts = host.Remote.Split(':');
                    service.Name = localConfig.Name;
                    service.HostName = host.Remote;
                    service.AddAdapter(new Net.Adapter.SerializeAdapter3());
                    service.SetConfig(Config.ServerConfig);
                    service.Start(int.Parse(parts[1]));
                    Services.Add(service);
                    Central.Call(ProtoType.RegisterDiscovery, localConfig.Name, host);
                }
            }
        }

        public static void StartDBCluster()
        {
            if (Config == null)
            {
                NDebug.LogError("请设置集群配置!");
                return;
            }
            if (DBCluster != null)
                return;
            DBCluster = new();
            DBCluster.Init(Config.DBConfig);
        }

        public static async Task ConnectCentral()
        {
            var client = CreateClient();
            var parts = Config.CentralHost.Split(':');
            client.Ip = parts[0];
            client.Port = int.Parse(parts[1]);
            var connected = await client.Connect();
            if (!connected)
                NDebug.LogError("连接中心服务器失败!");
            client.OnRpcInvoke = (model) =>
            {
                _ = CentralHandler(model);
            };
            Central = client;
        }

        private static async UniTask CentralHandler(IRpcModel model)
        {
            if ((ProtoType)model.Protocol == ProtoType.ConnectDiscoveryServer)
            {
                centralLock.Enter();
                var serviceRegistry = model.As<Dictionary<string, List<HostConfig>>>();
                for (int i = Services.Count - 1; i >= 0; i--)
                {
                    var service = Services[i];
                    foreach (var clusterConfig in serviceRegistry)
                    {
                        foreach (var host in clusterConfig.Value)
                        {
                            var cluster = GetCluster(service.Name, service.HostName, clusterConfig.Key);
                            cluster.Name = $"{service.Name}[{service.HostName}] => {clusterConfig.Key}[{host.Remote}]";
                            var client1 = await cluster.GetConnect(host.Remote, host.NodeName);
                            client1.Local = service.HostName;
                            client1.Remote = host.Remote;
                            client1.Route = $"{service.HostName}->{host.Remote}";
                            client1.Init();
                        }
                    }
                }
                centralLock.Exit();
            }
        }

        public static Cluster GetCluster<T>(this ServerBase self)
        {
            var toServer = typeof(T).ToString();
            return GetCluster(self.Name, self.HostName, toServer);
        }

        public static Cluster GetCluster(string serverType, string hostName, string toServer)
        {
            lock (centralLock)
            {
                if (!Clusters.TryGetValue(serverType, out var servers))
                    Clusters.Add(serverType, servers = []);
                if (!servers.TryGetValue(hostName, out var clusterGroup))
                    servers.Add(hostName, clusterGroup = []);
                if (!clusterGroup.TryGetValue(toServer, out var cluster))
                    clusterGroup.Add(toServer, cluster = new());
                return cluster;
            }
        }

        public static TcpNetwork CreateClient()
        {
            var client = ClientPool.Create(Config.ClientConfig);
            client.AddAdapter(new Net.Adapter.SerializeAdapter3());
            return client;
        }
    }
}
